#include "testcases.h"

#include <string>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <queue>

#include "engine_impl.h"
#include "engine_mock.h"
#include "parser.h"
#include "path.h"
#include "util.h"
#include "ipc_controller_mock.h"
#include "ipc_controller.h"
#include "ipc_manager.h"

#include <sys/wait.h>   /* Wait for Process Termination */

using std::string;

std::queue<string> IsExistReturn_;

void TEST_Path() {
  printf("Testing path.h...\n");
  string tests[] = {"@irvan", "@", "irvan"};
  bool expected_virtual[] = {false, false, true};
  string expected_paths[] = {"irvan", "", "irvan"};

  for (int i = 0; i < 3; ++i) {
    Path p(tests[i]);
    assert(expected_virtual[i] == p.is_virtual());
    assert(expected_paths[i] == p.path());
  }
}

void TEST_Parser() {
  printf("Testing parser.h...\n");
  string tests[] = {
    "format @irvan/jahja 237",
    "mountfs @irvan/jahja /test/",
    "umount /test/",
    "pwd",
    "ls",
    "ls irvan/",
    "cd irvan/",
    "rm a/",
    "cp @irvan irvan",
    "cp irvan jahja",
    "cp irvan @jahja",
    "mv @irvan irvan",
    "mv irvan @irvan",
    "mv irvan jahja",
    "cat irvan/jahja",
    "status",
    "exit"
  };

  Parser parser(new EngineMock());
  for (int i = 0; i < 17; ++i) {
    assert(tests[i] == parser.Execute(tests[i]));
  }
}

void TEST_Util() {
  printf("Testing util.h...\n");
  assert(IsDirectoryName("/"));
  assert(IsDirectoryName("/irvan/"));
  assert(!IsDirectoryName("/irvan"));
  assert(!IsDirectoryName(""));

  assert(!IsFileName("/"));
  assert(!IsFileName("/irvan/"));
  assert(IsFileName("/irvan"));
  assert(!IsFileName(""));

  assert(0 == StringToInt("0"));
  assert(10 == StringToInt("10"));

  assert("0" == IntToString(0));
  assert("10" == IntToString(10));

  assert("/irvan/" == GetFolder("/irvan/jahja.txt"));
  assert("/irvan/jahja/" == GetFolder("/irvan/jahja/ganteng/"));
}

void TEST_EngineImpl() {
  printf("Testing engine_impl.h...\n");
  EngineImpl engine;

  // MountFS tests
  assert("/" == engine.Pwd());
  string result;
  result = engine.MountFSTest(
          Path("non_existant.disk", false), Path("dev1/", true));
  assert(strstr(result.c_str(), "non_existant.disk not found") != NULL);

  result = engine.MountFSTest(
          Path("dummy_test_empty.disk", false), Path("dev1/", true));
  assert(strstr(result.c_str(),
         "dummy_test_empty.disk successfully mounted on /dev1/") != NULL);

  IsExistReturn_.push("YES");
  result = engine.MountFSTest(
          Path("dummy_test_empty.disk", false), Path("dev1/", true));
  assert(strstr(result.c_str(), "Directory /dev1/ already exists!") != NULL);

  result = engine.MountFSTest(
          Path("dummy_test_empty.disk", false), Path("day1/day2/day3/", true));
  assert(strstr(result.c_str(), "Mountfs can only") != NULL);

  result = engine.MountFSTest(
          Path("dummy_test_empty.disk", false), Path("dev2/", true));
  assert(strstr(result.c_str(),
         "dummy_test_empty.disk successfully mounted on /dev2/") != NULL);

  // Unmount Tests
  result = engine.Unmount(Path("dev2/", true));
  assert(strstr(result.c_str(),
                "umount. Current") != NULL);

  result = engine.Unmount(Path("dev2/", true));
  assert(strstr(result.c_str(),
                "No device is mounted on /dev2/") != NULL);

  result = engine.MountFSTest(
          Path("dummy_test_empty.disk", false), Path("dev2/", true));
  assert(strstr(result.c_str(),
         "dummy_test_empty.disk successfully mounted on /dev2/") != NULL);

  // Ls tests
  IsExistReturn_.push("YES");
  assert("ls /" == engine.LsDirectory(Path("dev1/", true)));

  IsExistReturn_.push("NO");
  assert("Directory /dev1/not_exist/ does not exists!" ==
         engine.LsDirectory(Path("dev1/not_exist/", true)));

  assert("/dev1/\n/dev2/" == engine.Ls());

  // Cd tests
  IsExistReturn_.push("NO");
  assert("Directory /dev1/not_exist/ does not exists!" ==
         engine.Cd(Path("dev1/not_exist/", true)));

  IsExistReturn_.push("YES");
  assert("Current directory changed to /dev1/" ==
         engine.Cd(Path("/dev1/", true)));

  IsExistReturn_.push("YES");
  assert("ls /" == engine.Ls());

  // Mkdir tests
  IsExistReturn_.push("NO");
  IsExistReturn_.push("YES");
  assert("mkdir /folder/" == engine.Mkdir(Path("folder/", true)));

  IsExistReturn_.push("YES");
  assert("Directory /dev1/folder/ already exists!" ==
         engine.Mkdir(Path("folder/", true)));

  IsExistReturn_.push("NO");
  IsExistReturn_.push("NO");
  assert("The directory containing /dev1/ne/ane/ does not exists!" ==
         engine.Mkdir(Path("ne/ane/", true)));

  // Rm tests
  IsExistReturn_.push("NO");
  assert("/dev1/a.txt does not exists!" == engine.Rm(Path("a.txt", true)));

  IsExistReturn_.push("NO");
  assert("/dev1/a/ does not exists!" == engine.Rm(Path("/dev1/a/", true)));

  assert("You cannot remove root" == engine.Rm(Path("/", true)));

  IsExistReturn_.push("YES");
  assert("rm /a/b/" == engine.Rm(Path("a/b/", true)));

  // Cp tests
  IsExistReturn_.push("NO");
  assert("/dev1/a does not exists!" ==
         engine.Cp(Path("a", true), Path("b", true)));

  IsExistReturn_.push("YES");
  IsExistReturn_.push("YES");
  assert("/dev1/b already exist!" ==
         engine.Cp(Path("a", true), Path("b", true)));

  IsExistReturn_.push("YES");
  assert("/ already exist!" ==
         engine.Cp(Path("a/", true), Path("/", true)));

  IsExistReturn_.push("NO");
  assert("You cannot copy to or from root" ==
         engine.Cp(Path("/", true), Path("a/", true)));

  IsExistReturn_.push("YES");
  IsExistReturn_.push("NO");
  assert("/dev1/a and /dev1/b/ must be either both files or both folders" ==
         engine.Cp(Path("a", true), Path("b/", true)));

  IsExistReturn_.push("YES");
  result = engine.Cp(
          Path("a", true), Path("benda_aneh.disk", false));
  assert(strstr(result.c_str(), "cp1 /a ") != NULL);

  IsExistReturn_.push("NO");
  IsExistReturn_.push("YES");
  result = engine.Cp(
          Path("dummy_test_empty.disk", false), Path("a", true));
  assert(strstr(result.c_str(), "cp2") != NULL);
  assert(strstr(result.c_str(), ".disk /a") != NULL);

  assert(IsExistReturn_.empty());
  IsExistReturn_.push("YES");
  IsExistReturn_.push("NO");
  IsExistReturn_.push("NO");
  assert("The folder containing /dev1/a/b must exists!" ==
         engine.Cp(Path("b", true), Path("a/b", true)));

  assert(IsExistReturn_.empty());
  IsExistReturn_.push("YES");
  IsExistReturn_.push("NO");
  IsExistReturn_.push("YES");
  result = engine.Cp(
          Path("b", true), Path("a", true));
  assert(strstr(result.c_str(), "cp1 /b ") != NULL);
  assert(strstr(result.c_str(),
                "VERY_UGLY_TEMPORARY_FILE_NAME.TMP\ncp2 ") != NULL);
  assert(strstr(result.c_str(),
                "VERY_UGLY_TEMPORARY_FILE_NAME.TMP /a"));

  IsExistReturn_.push("YES");
  IsExistReturn_.push("NO");
  IsExistReturn_.push("YES");
  result = engine.Cp(
          Path("b/", true), Path("a/", true));
  assert(strstr(result.c_str(), "cp1 /b/ ") != NULL);
  assert(strstr(result.c_str(),
                "VERY_UGLY_TEMPORARY_FOLDER_NAME\ncp2 ") != NULL);
  assert(strstr(result.c_str(),
                "VERY_UGLY_TEMPORARY_FOLDER_NAME /a/"));

  // Mv tests
  assert(IsExistReturn_.empty());
  IsExistReturn_.push("YES");
  IsExistReturn_.push("NO");
  IsExistReturn_.push("YES");
  IsExistReturn_.push("YES");
  result = engine.Mv(
          Path("b", true), Path("a", true));
  assert(strstr(result.c_str(), "cp1 /b ") != NULL);
  assert(strstr(result.c_str(),
                "VERY_UGLY_TEMPORARY_FILE_NAME.TMP\ncp2 ") != NULL);
  assert(strstr(result.c_str(),
                "VERY_UGLY_TEMPORARY_FILE_NAME.TMP /a\n") != NULL);
  assert(strstr(result.c_str(),
                "\nrm /b") != NULL);

  // Cat tests
  IsExistReturn_.push("NO");
  assert("File /dev1/a.txt does not exists!" == engine.Cat(Path("a.txt", true)));

  IsExistReturn_.push("YES");
  assert("cat /a/b.txt" == engine.Cat(Path("a/b.txt", true)));

  // Status
  assert("mount_point='/dev1/' status\nmount_point='/dev2/' status" ==
         engine.Status());

  // Exit
  printf("%s\n", engine.Exit().c_str());

  // This one must remain at the end of the tests.
  assert(IsExistReturn_.size() == 0);
}

void TEST_IPCController() {
  printf("Testing ipc_controller.h...\n");

  int msg_flag = 0666;

  printf("Creating object...\n");
  IPCController object(53, 54);

  int idsend = msgget(53, msg_flag);
  int idrec = msgget(54, msg_flag);

  printf("Creating manager...\n");
  IPCManager manager(idrec, idsend);

  printf("IsExist YES\n");

  manager.SendMessage("YES");
  assert(object.IsExist("/dev1/irvan/"));
  assert(manager.ReceiveMessage() == "isexist");
  assert(manager.ReceiveMessage() == "/dev1/irvan/");

  printf("IsExist NO\n");

  manager.SendMessage("NO");
  assert(!object.IsExist("/dev1/jahja"));
  assert(manager.ReceiveMessage() == "isexist");
  assert(manager.ReceiveMessage() == "/dev1/jahja");

  printf("ls\n");
  manager.SendMessage("LS");
  assert("LS" == object.Ls("/dev1/jahja"));
  assert(manager.ReceiveMessage() == "ls");
  assert(manager.ReceiveMessage() == "/dev1/jahja");

  printf("mkdir\n");
  manager.SendMessage("MKDIR");
  assert("MKDIR" == object.Mkdir("/dev1/jahja"));
  assert(manager.ReceiveMessage() == "mkdir");
  assert(manager.ReceiveMessage() == "/dev1/jahja");

  printf("rm\n");
  manager.SendMessage("RM");
  assert("RM" == object.Rm("/dev1/jahja"));
  assert(manager.ReceiveMessage() == "rm");
  assert(manager.ReceiveMessage() == "/dev1/jahja");

  printf("cp1\n");
  manager.SendMessage("CP1");
  assert("CP1" == object.Cp1("/dev1/1", "/dev2/2"));
  assert(manager.ReceiveMessage() == "cp1");
  assert(manager.ReceiveMessage() == "/dev1/1");
  assert(manager.ReceiveMessage() == "/dev2/2");

  printf("cp2\n");
  manager.SendMessage("CP2");
  assert("CP2" == object.Cp2("/dev1/1", "/dev2/2"));
  assert(manager.ReceiveMessage() == "cp2");
  assert(manager.ReceiveMessage() == "/dev1/1");
  assert(manager.ReceiveMessage() == "/dev2/2");

  printf("cat\n");
  manager.SendMessage("CAT");
  assert("CAT" == object.Cat("/dev1/jahja"));
  assert(manager.ReceiveMessage() == "cat");
  assert(manager.ReceiveMessage() == "/dev1/jahja");

  printf("close\n");
  manager.SendMessage("CLOSE");
  assert("CLOSE" == object.Close());
  assert(manager.ReceiveMessage() == "close");

  printf("status\n");
  manager.SendMessage("STATUS");
  assert("STATUS" == object.Status());
  assert(manager.ReceiveMessage() == "status");
}
